#include<linux/module.h>
#include<linux/init.h>
#include<linux/fs.h>
#include<linux/cdev.h>
#include<linux/device.h>
#include<linux/platform_device.h>
#include<linux/mod_devicetable.h>
#include<linux/gpio.h>
#include<linux/of.h>
#include<linux/of_gpio.h>
#include<linux/uaccess.h>
#include<linux/i2c.h>
#include<linux/bcd.h>
#include<linux/rtc.h>
#include<linux/regmap.h>
#include<linux/pm_wakeirq.h>


/*寄存器地址*/

#define REG_SEC 0x02
#define REG_MIN 0x03
#define REG_HOUR 0x04
#define REG_DAY 0x05
#define REG_WEEK 0x06
#define REG_MONTH 0x07
#define REG_YEAR 0x08
#define NUM_TIME_REGS (REG_YEAR - REG_SEC + 1)

#define REG_CON1 0x01

#define REG_MIN_ALM 0x09
#define REG_HOUR_ALM 0x0a
#define REG_DAY_ALM 0x0b
#define REG_WEEK_ALM 0x0c
#define NUM_ALM_REGS (REG_WEEK_ALM - REG_MIN_ALM + 1)


struct jx260 {
    /*注册的rtc设备*/
    struct rtc_device *rtc;
    struct regmap	*regmap;
	int c_polarity;
};


static int jx260_rtc_read_time(struct device *dev, struct rtc_time *tm)
{
    unsigned char rtc_data[NUM_TIME_REGS] = {0};
	int ret;
	struct jx260 *jx260 = dev_get_drvdata(dev);

    printk("read status\n");

    ret = regmap_bulk_read(jx260->regmap, REG_SEC, rtc_data, NUM_TIME_REGS);
    if (ret < 0) {
        dev_err(dev, "use I2C reading time from RTC failed with err:%d\n", ret);
        return ret;
    }
	printk("regmap_bulk_read\n");

	//掉电检测，在set_time清零,因为时钟不准就要重新设置时钟
	if(rtc_data[REG_SEC - 2] & 0x80)
	{
		printk("data is error\n");
		return -1;
	}
	
    /*取前七位*/
    tm->tm_sec = bcd2bin(rtc_data[REG_SEC - 2] & 0x7F);
	tm->tm_min = bcd2bin(rtc_data[REG_MIN - 2] & 0x7F);

    /*取前六位*/
	tm->tm_hour = bcd2bin(rtc_data[REG_HOUR - 2] & 0x3F); 
	tm->tm_mday = bcd2bin(rtc_data[REG_DAY - 2] & 0x3F);

    /*星期，星期一般不需要管，因为确定年月日之后星期也是确认的了*/
	tm->tm_wday = rtc_data[REG_WEEK - 2] & 0x07;
	
    tm->tm_mon = bcd2bin(rtc_data[REG_MONTH - 2] & 0x1F) - 1;
	tm->tm_year = bcd2bin(rtc_data[REG_YEAR - 2]) + 100;

	//保持jx260->c_polarity与系统设置时间统一
	jx260->c_polarity = (rtc_data[REG_MONTH - 2] & 0x80) ? (tm->tm_year >= 100) : (tm->tm_year < 100);

    return 0;
}


static int jx260_rtc_set_time(struct device *dev, struct rtc_time *tm)
{
	unsigned char rtc_data[NUM_TIME_REGS];
//	struct jx260 *jx260 = i2c_get_clientdata(client);
	int ret;

	struct jx260 *jx260 = dev_get_drvdata(dev);

    printk("set time\n");
	rtc_data[REG_SEC - 2] = bin2bcd(tm->tm_sec);
	rtc_data[REG_MIN - 2] = bin2bcd(tm->tm_min);
	rtc_data[REG_HOUR - 2] = bin2bcd(tm->tm_hour);
    rtc_data[REG_DAY - 2] = bin2bcd(tm->tm_mday);

    /*1 ~ 12*/
    rtc_data[REG_MONTH - 2] = bin2bcd(tm->tm_mon + 1);


    if ((tm->tm_year < 100) || (tm->tm_year > 199)) {
        dev_err(dev, "the actual setting year should be between 2000 and 2099!\n");
        return -EINVAL;
    }
    /*2000 ~ 2099*/
    rtc_data[REG_YEAR - 2] = bin2bcd(tm->tm_year - 100);
	if(jx260->c_polarity ? (tm->tm_year >= 100) : (tm->tm_year < 100))
	{
		rtc_data[REG_MONTH - 2] |= 0x80;
	}

    /*0 ~ 6*/
    rtc_data[REG_WEEK - 2] = (1 << tm->tm_wday);

    ret = regmap_bulk_write(jx260->regmap, REG_SEC, rtc_data, NUM_TIME_REGS);
    if (ret < 0) {
        dev_err(dev, "use I2C writing to RTC failed with err:%d\n", ret);
        return ret;
    }
    return 0;
}


static int jxr260_alarm_irq_enable(struct device *dev, unsigned int enabled)
{
	struct jx260 *jx260 = dev_get_drvdata(dev);
	unsigned char rtc_data;
	int ret;
	printk("alarm irq enable\n");
	
    ret = regmap_bulk_read(jx260->regmap, REG_CON1, &rtc_data, 1);
    if (ret < 0) {
        dev_err(dev, "use I2C reading time from RTC failed with err:%d\n", ret);
        return ret;
    }
	/*enabled=1,将AIE位置1开启中断；enabled=0,将AIE位置0关闭中断*/
	rtc_data = enabled ? (rtc_data | 0x02) : (rtc_data & 0xfd);
	return regmap_bulk_write(jx260->regmap, REG_CON1, &rtc_data, 1);
}



static int jxr260_read_alarm(struct device *dev, struct rtc_wkalrm *wkalrm)
{
	struct rtc_time *alrm_tm = &wkalrm->time;
	struct jx260 *jx260 = dev_get_drvdata(dev);
	unsigned char rtc_data;
	unsigned char alrm_data[4] = {0};
	int ret;
	printk("read alarm\n");



	
	ret = regmap_bulk_read(jx260->regmap, REG_MIN_ALM, alrm_data, NUM_ALM_REGS);
	if (ret < 0) {
		dev_err(dev, "use I2C reading time from RTC failed with err:%d\n", ret);
		return ret;
	}

	alrm_tm->tm_min = bcd2bin(alrm_data[0] & 0x7f);
	alrm_tm->tm_hour = bcd2bin(alrm_data[1] & 0x3f);
	alrm_tm->tm_mday = bcd2bin(alrm_data[2] & 0x3f);
	alrm_tm->tm_wday = bcd2bin(alrm_data[3] & 0x07);

	ret = regmap_bulk_read(jx260->regmap, REG_CON1, &rtc_data, 1);
	if (ret < 0) {
		dev_err(dev, "use I2C reading time from RTC failed with err:%d\n", ret);
		return ret;
	}

	wkalrm->enabled = (rtc_data & 0x02) ? 1 : 0;

	return 0;
}

static int jxr260_set_alarm(struct device *dev, struct rtc_wkalrm *wkalrm)
{
	struct rtc_time *alrm_tm = &wkalrm->time;
	struct jx260 *jx260 = dev_get_drvdata(dev);
	unsigned char alrm_data[4] = {0};
	int ret;
	printk("set alarm\n");

	ret = jxr260_alarm_irq_enable(dev, 0);
	if(ret)
	{
		printk("stop alarm irq error\n");
		return ret;
	}

	alrm_data[0] = alrm_tm->tm_min ? alrm_tm->tm_min : (0x01 << 7);
	alrm_data[1] = alrm_tm->tm_hour ? alrm_tm->tm_hour : (0x01 << 7);
	alrm_data[2] = alrm_tm->tm_mday ? alrm_tm->tm_mday : (0x01 << 7);
	alrm_data[3] = alrm_tm->tm_wday ? alrm_tm->tm_wday : (0x01 << 7);

	alrm_data[0] = bin2bcd(alrm_data[0]);
	alrm_data[1] = bin2bcd(alrm_data[1]);
	alrm_data[2] = bin2bcd(alrm_data[2]);
	alrm_data[3] = bin2bcd(alrm_data[3]);
	
	
	ret = regmap_bulk_write(jx260->regmap, REG_MIN_ALM, alrm_data, NUM_ALM_REGS);
	if (ret < 0) {
		dev_err(dev, "use I2C writeing time from RTC failed with err:%d\n", ret);
		return ret;
	}

	
	ret = jxr260_alarm_irq_enable(dev, 1);
	if(ret)
	{
		printk("status alarm irq error\n");
		return ret;
	}
	
	return 0;
}


//中断回调函数
//一般的操作为 1、清除RTC的中断标志位 2、调用rtc_update_irq，异步通知上报给应用层，应用层使用poll函数等待
static irqreturn_t rtc_handle_irq(int irq, void *dev_id)
{
        struct i2c_client *client = dev_id;
        struct jx260 *jx260 = i2c_get_clientdata(client);
        unsigned char rtc_data[1];

		//清除AF位
        regmap_bulk_read(jx260->regmap, REG_CON1, rtc_data, 1);
        rtc_data[0] &= ~(0x01 << 3);
        regmap_bulk_write(jx260->regmap, REG_CON1, rtc_data, 1);
		
		//异步通知上报应用层
        rtc_update_irq(jx260->rtc, 1, RTC_IRQF | RTC_AF);

        return IRQ_HANDLED;
}


static const struct rtc_class_ops jx260_rtc_ops ={
    .read_time	= jx260_rtc_read_time,
	.set_time	= jx260_rtc_set_time,
	.read_alarm = jxr260_read_alarm,
	.set_alarm	= jxr260_set_alarm,
	.alarm_irq_enable = jxr260_alarm_irq_enable
};

static const struct regmap_config regmap_config = {
	.reg_bits = 8,
	.val_bits = 8,
	.max_register = 0x14,
};


static int jx260_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
    struct jx260 *jx260;
    int ret;
	struct gpio_desc *gpiod;
	struct device_node *np = client->dev.of_node;

    printk("staus probe\n");


    if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
        dev_err(&client->dev, "I2c is not available or there are issues with its functionality\n");
        return -ENODEV;
    }

    /*申请空间*/
    jx260 = devm_kzalloc(&client->dev, sizeof(*jx260), GFP_KERNEL);
    if(!jx260)
    {
        dev_err(&client->dev, "device memory request failed\n");
		return -ENOMEM;
    }
    printk("devm\n");


    jx260->regmap = devm_regmap_init_i2c(client, &regmap_config);
	if (IS_ERR(jx260->regmap)) {
		dev_err(&client->dev, "I2C initialization regmap failed\n");
		return PTR_ERR(jx260->regmap);
	}

    i2c_set_clientdata(client, jx260);

    /*初始化jx260*/
	regmap_write(jx260->regmap, 0x0e, 0x03);
	regmap_write(jx260->regmap, 0x01, 0x00);
	
    printk("regmap\n");
    /*申请注册rtc设备*/
    jx260->rtc = devm_rtc_allocate_device(&client->dev);
        if (IS_ERR(jx260->rtc)) {
                dev_err(&client->dev, "alloc rtc device failed\n");
                return PTR_ERR(jx260->rtc);
        }

    jx260->rtc->ops = &jx260_rtc_ops;
    jx260->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000;
	jx260->rtc->range_max = RTC_TIMESTAMP_END_2099;
	dev_set_drvdata(&jx260->rtc->dev ,jx260);
    
    printk("ops\n");
	
	/*注册的过程中会调用ops->read_time，验证ops->read_time可不可用*/
    ret = devm_rtc_register_device(jx260->rtc);
    if(ret)
    {
        dev_err(&client->dev, "rtc device registration failed\n");
        return ret;
    }

	//获取irq-gpios属性，申请gpio并将gpio配置为输出模式
	gpiod = devm_gpiod_get(&client->dev, "irq", GPIOD_IN);
	client->irq = gpiod_to_irq(gpiod);
	
	if (client->irq < 0) {
			  dev_err(&client->dev, "interrupt number conversion failed\n");
//			  return client->irq;
	 }
	
	  if (client->irq > 0) {
	  			//申请中断，低电平触发
			  ret = devm_request_threaded_irq(&client->dev, client->irq, NULL, rtc_handle_irq,
											  IRQF_TRIGGER_LOW | IRQF_ONESHOT,
											  "jxr260", client);
			  if (ret)
					  dev_err(&client->dev, "unable to request IQR, client->irq = %d\n", client->irq);
	  }


	 //如果存在wakeup-source那么就支持唤醒功能
	if (of_property_read_bool(np, "wakeup-source")) 
	{
		device_init_wakeup(&client->dev, 1); //函数作用是告诉内核支持唤醒功能
		dev_pm_set_wake_irq(&client->dev, client->irq);  //绑定中断与该设备，使PM子系统自动管理电源
	}
	
	
    printk("register rtc\n");

    return 0;
}


static const struct i2c_device_id jx260_id[] = {
	{"jx260", 0},
	{ }
};
MODULE_DEVICE_TABLE(i2c, jx260_id);


static const __maybe_unused struct of_device_id rtc_dt_ids[] = {
	{.compatible = "pcf8563"},
	{},
};
MODULE_DEVICE_TABLE(of, rtc_dt_ids);



struct i2c_driver jx260_driver = {
    .driver = {
        .name = "rtc-pcf8563",
        .of_match_table = of_match_ptr(rtc_dt_ids)
    },
    .probe = jx260_probe,
    .id_table = jx260_id

};

static int __init jx260_init(void)
{
    int ret;
    ret = i2c_add_driver(&jx260_driver);

    printk("pcf8563 driver register\n");
    return ret;
}


static void __exit jx260_exit(void)
{
    i2c_del_driver(&jx260_driver);
    printk("pcf8563 driver unregister\n");
}


module_init(jx260_init);
module_exit(jx260_exit);
MODULE_LICENSE("GPL");
